﻿using Microsoft.VisualStudio.TestTools.UnitTesting;

using DatabaseGroup.SSIS.SourceAdapters.Parser;

namespace DatabaseGroup.UnitTests.SSIS.SourceAdapters.Parser
{
    [TestClass()]
    public class MatchTokenParsingStateTest
    {
        private const string _expectedMissParsingResult =
            "Expected parsing result to be Miss based on not matching match token";
        private const string _expectedDoneParsingResult =
            "Expected parsing result to be Done based on completely matching whole match token with no further states";
        private const string _expectedMatchParsingResult =
            "Expected parsing result to be Match based on partially matching match token";
        private TestContext _testContextInstance;

        public TestContext TestContext
        {
            get
            {
                return _testContextInstance;
            }
            set
            {
                _testContextInstance = value;
            }
        }

        [TestMethod()]
        [ExpectedException(typeof(System.ArgumentNullException), "Expected ArgumentNullException to be thrown when called with empty arg")]
        public void MatchTokenParsingState_ConstructorWithEmptyArg_ThrowArgumentNullException()
        {
            MatchTokenParsingState target = new MatchTokenParsingState(string.Empty);
        }

        [TestMethod()]
        [ExpectedException(typeof(System.ArgumentNullException), "Expected ArgumentNullException to be thrown when called with null arg")]
        public void ProcessCharacter_WithNullArg_ThrowArgumentNullException()
        {
            MatchTokenParsingState target = new MatchTokenParsingState(",");
            target.ProcessCharacter(null, 'a');
        }

        [TestMethod()]
        public void ProcessCharacter_SingleCharToken_ReturnExpectedParsingResult()
        {
            ParsingContextTestImpl context = new ParsingContextTestImpl();
            MatchTokenParsingState target = new MatchTokenParsingState(",");
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'a'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'c'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Done, target.ProcessCharacter(context, ','), _expectedDoneParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'x'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Done, target.ProcessCharacter(context, ','), _expectedDoneParsingResult);
        }

        [TestMethod()]
        public void ProcessCharacter_MultiCharToken_ReturnExpectedParsingResult()
        {
            ParsingContextTestImpl context = new ParsingContextTestImpl();
            MatchTokenParsingState target = new MatchTokenParsingState(",;:!");
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'a'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'c'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'x'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ';'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Done, target.ProcessCharacter(context, '!'), _expectedDoneParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'y'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ';'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'z'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ';'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Done, target.ProcessCharacter(context, '!'), _expectedDoneParsingResult);
        }

        [TestMethod()]
        public void ProcessCharacter_MultiCharTokenWithNextState_ReturnExpectedParsingResultAndCorrectState()
        {
            ParsingContextTestImpl context = new ParsingContextTestImpl();
            ParsingStateTestImpl nextState = new ParsingStateTestImpl(ParsingResult.Match);
            MatchTokenParsingState target = new MatchTokenParsingState(",;:!");
            target.AddNextState(nextState);

            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'a'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'c'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'x'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ';'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, '!'), _expectedMatchParsingResult);
            Assert.AreEqual<IParsingState>(nextState, context.CurrentState, "Expected parsing context state to change to next state based on matching complete token");
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'y'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ';'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'z'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ','), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ';'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, '!'), _expectedMatchParsingResult);
            Assert.AreEqual<IParsingState>(nextState, context.CurrentState, "Expected parsing context state to change to next state based on matching complete token");
        }

        [TestMethod()]
        public void ProcessCharacter_MultiCharTokenWithNextStateAndErrorState_ReturnExpectedParsingResultAndCorrectState()
        {
            ParsingContextTestImpl context = new ParsingContextTestImpl();
            ParsingStateTestImpl nextState = new ParsingStateTestImpl(ParsingResult.Match);
            ParsingStateTestImpl errorState = new ParsingStateTestImpl(ParsingResult.Match);

            MatchTokenParsingState target = new MatchTokenParsingState(":=>");
            target.AddNextState(nextState);
            target.SetErrorState(errorState);

            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'f'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 't'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'x'), _expectedMissParsingResult);
            Assert.AreEqual<IParsingState>(errorState, context.CurrentState, "Expected parsing context state to be in error state based on malformed token");
            Assert.AreEqual<string>(":", context.CurrentText, "Expected current text value to be initial match data of malformed token");
            context.ResetText();
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, '='), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, '>'), _expectedMatchParsingResult);
            Assert.AreEqual<IParsingState>(nextState, context.CurrentState, "Expected parsing context state to change to next state based on matching complete token");
            Assert.AreEqual<string>(string.Empty, context.CurrentText, "Expected current text value to be empty based on successfully matching whole token");
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'y'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Miss, target.ProcessCharacter(context, 'm'), _expectedMissParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, ':'), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, '='), _expectedMatchParsingResult);
            Assert.AreEqual<ParsingResult>(ParsingResult.Match, target.ProcessCharacter(context, '>'), _expectedMatchParsingResult);
            Assert.AreEqual<IParsingState>(nextState, context.CurrentState, "Expected parsing context state to change to next state based on matching complete token");
            Assert.AreEqual<string>(string.Empty, context.CurrentText, "Expected current text value to be empty based on successfully matching whole token");
        }
    }
}
